//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler execution subsystem
// FILE:            xcode.cpp - all stuff related to generated code
// AUTHOR:          Vladimir Gumenuk
// DATE:            12-Dec-2012
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"
#include "xcode.h"
#include <limits.h>
#include "listImpl.h"

struct LabelInfo
{
    dword dwNumber;    // the number of label assigned by the compiler
    dword dwOldAddr;
    dword dwNewAddr;   // new address in the copied code
    dword dwIndex;     // index of the current label in the code
    int   bUsedFlag;
    LabelInfo() {
        dwNumber = 0; dwOldAddr = 0; dwNewAddr = 0; bUsedFlag = 0; dwIndex = 0;
    }
    void operator= ( int i ) {
        dwNumber = (dword)i; dwOldAddr = (dword)i; dwNewAddr = (dword)i; bUsedFlag = i; dwIndex = (dword)i;
    }
    void operator= ( const LabelInfo& l  ) {
        dwNumber = l.dwNumber; dwOldAddr = l.dwOldAddr; dwNewAddr = l.dwNewAddr; bUsedFlag = l.bUsedFlag; dwIndex = l.dwIndex;
    }
};

typedef xarray< LabelInfo, CCopySimpleType<LabelInfo>, CDeleteSimpleType<LabelInfo> > LABELS_ARRAY;

static inline LabelInfo* findLabelByNumber( const LABELS_ARRAY& arLabels, dword dwNumber )
{
    for( dword i = 0; i < arLabels.count(); i++ ) {
        LabelInfo& Info = arLabels.get( i );
        if( Info.dwNumber == dwNumber )
            return &Info;
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////////
// Removes all labels pseudo code and updates goto and ifXX instructions with
// valid relative offsets.
// Offset is calculated from the current IP:
//      0038: do_somethig
//      ...
//      0045: goto do_somethig
//  Offset = 38 - 45 = -7; XVM calculates the target like the following:
//  Target = 45 + (-7) = 38;, and continue execution from this point.
int xc_removeLabels( SymbolRef proc )
{
    if( proc.m_dwFlags & f_labels_removed ) // very early return
        return XORT_OK;

    dword dwIP       = 0;            // actual IP
    dword dwLineIdx  = 0;            // index of line
    dword dwInstrIdx = 0;            // index of instruction
    dword dwNumInstructions = proc.m_Code.count();

    if( dwNumInstructions == 0 ) // this can be build-in or native proc
        return XORT_OK;

    LABELS_ARRAY        arLabels;   // array of all labels in the current proc
    INSTRUCTION_ARRAY   arCode;     // new code array

    //////////////////////////////////////////////////////////////////////////
    while( dwIP < dwNumInstructions ) // first pass: scan for all labels and make a copy of the original code without labels
    {
        InstructionData *pDecData = proc.m_Code.get( dwIP );

        if( pDecData->m_bInstrCode == p_label ) // this pseudo instruction consists of 3 bytes: code + label_No_u2
        {
            LabelInfo lblInf;
            lblInf.dwOldAddr = dwIP;
            lblInf.dwNumber  = pDecData->m_Value._u4;   // currently compiler generates only _u2 offsets, but in the future it can be changed
            lblInf.dwNewAddr = arCode.count();        // the size of the currently added bytes - is the actual place where label is pointing to
            lblInf.dwIndex   = dwInstrIdx;            // save index of next instruction that label points on
            arLabels.add( lblInf );
        } else if( pDecData->m_bInstrCode == p_stkvar ) {
            ; // do nothing, just remove stkvar pesudo instruction...
        } else { // else just copy the code
            arCode.add( pDecData );
            ++dwInstrIdx;
        }

        ++dwIP;
    }

    if( arLabels.count() == 0 ) { // do not proceed if function does not have any labels
        arCode.copy( proc.m_Code );
        arCode.clear();
        proc.m_dwFlags |= f_labels_removed;
        proc.m_dwFlags |= f_instr_decoded;
        return XORT_OK;
    }

    dwNumInstructions = arCode.count(); // new code will be smaller on NumLabels * 3 bytes
    dwIP = 0;
    dwInstrIdx = 0;
    //////////////////////////////////////////////////////////////////////////
    while( dwIP < dwNumInstructions ) // second pass: update goto instructions with the actual offsets to a new label
    {
        InstructionData *pDecData = arCode.get( dwIP );
        byte bPrim = pDecData->m_bInstrCode;

        if( bPrim == p_goto  || bPrim == p_ifz   || bPrim == p_ifnz || bPrim == p_ifeq || bPrim == p_ifneq || 
            bPrim == p_ifleq || bPrim == p_ifgeq || bPrim == p_ifg  || bPrim == p_ifl )
        {
            byte bType = pDecData->m_bTypeAddr & 0xF0;
            dword dwLableNo;
            if( bPrim == p_goto ) {
                switch( bType ) // get label number
                { // initially goto instruction contains the unsigned label numbers
                case t_s1: dwLableNo = (byte)pDecData->m_Value._s1; break;
                case t_s2: dwLableNo = (word)pDecData->m_Value._s2; break;
                case t_s4: dwLableNo = (dword)pDecData->m_Value._s4; break;
                default: return ERR_INVALID_PRIMCODE;
                }
            } else { // ifXX instruction contains the u2 label numbers
                dwLableNo = pDecData->m_Value._u2;
            }

            LabelInfo* pLabel = findLabelByNumber( arLabels, dwLableNo ); // find label by it original number
            if( !pLabel ) // someone generated bad code :)
                return ERR_LABEL_NOT_FOUND;

            pLabel->bUsedFlag = TRUE; // mark that label has a target
            
            pDecData->m_Value._u4 = pLabel->dwIndex; // new interpreter requires the absolute index of the instructions.
        }

        ++dwInstrIdx;
        ++dwIP;
    }

    //////////////////////////////////////////////////////////////////////////
    // final pass: copy new code to the procedure
    arCode.copy( proc.m_Code );

    arLabels.clear();
    arCode.clear();

    proc.m_dwFlags |= f_labels_removed;
    proc.m_dwFlags |= f_instr_decoded;

    return XORT_OK;
}

